#pragma once
namespace graph
{
    namespace details
    {
    
    }
inline vertex_t const & graph_view_t::operator [] (size_t idx) const
{
   return (*graph_ptr_)[idx];
}
inline vertex_t& graph_view_t::operator [] (size_t idx)
{
   return (*graph_ptr_)[idx];
}
inline vertex_t const & graph_view_t::get_vertex_by_id(size_t idx) const
{
   return (*graph_ptr_)[idx];
}
inline vertex_t & graph_view_t::get_vertex_by_id(size_t idx)
{
   return (*graph_ptr_)[idx];
}
//O(|V|)
inline bool graph_view_t::get_nearest_vertex(point_t p, vertex_id_t& result) const
{
   vertex_id_t nearest_id = 0;
   lat_lon_t min_distance = MAX_FLOAT;
   lat_lon_t temp_distance;
   point_t temp_point;
   bool entered = false;
   view_vertex_iterator_pair_t v_it = vertices(const_cast<graph_view_t&>(*this));
   for(; v_it.first != v_it.second; ++v_it.first)
   {
      entered = true;
      temp_point = (*v_it.first).get_point();
      temp_distance = math::distance(temp_point.lat_, temp_point.lon_, p.lat_, p.lon_);
      if (temp_distance < min_distance)
      {
         min_distance = temp_distance;
         nearest_id = (*v_it.first).get_id();
      }
   }  
   result = nearest_id;
   return entered;
}

//O(1)
inline bool graph_view_t::get_nearest_vertices(point_t p, std::vector<vertex_id_t> & result) const
{
   details::cell_id_t cell_id = details::graph_metadata_t::get_cell_id<details::d1>(p);
   details::cell_vertex_iterator_pair_t it = graph_ptr_->get_metadata().cell_vertices(cell_id);
   for(; it.first != it.second; ++it.first)
   {
      result.push_back((*it.first).second);
   }
   if (!result.empty())
      return true;
   return false;
}

//O(1)
//Continuous, returns list of nearest edges to given point p. 
//All distances to edges are less then particular radius.
//Nearest points on edges produces something like a circle with this radius.
inline bool graph_view_t::get_nearest_edges(point_t p, std::vector<edge_t> & result) const
{
   for(int d = 1; d <= 10; d += 1)
   {
      for(int i = -d; i <= d; ++i)
         for(int j = -d; j <= d; ++j)
         {
            if (abs(i) != d && abs(j) != d)
               continue;
            details::cell_id_t cell_id = details::graph_metadata_t::get_cell_id<details::d1>(p);
            cell_id.first += i; cell_id.second += j;
            details::cell_edge_iterator_pair_t it = graph_ptr_->get_metadata().cell_edges<details::d1>(cell_id);
            for(; it.first != it.second; ++it.first)
            {
               if (!edge_predicate(it.first->second))
                  continue;
               result.push_back( (*it.first).second );
            } 
         }
      if (!result.empty())
         return true;
      for(int i = -d; i <= d; ++i)
         for(int j = -d; j <= d; ++j)
         {     
            if (abs(i) != d && abs(j) != d)
               continue;
            details::cell_id_t cell_id = details::graph_metadata_t::get_cell_id<details::d2>(p);
            cell_id.first += i; cell_id.second += j;
            details::cell_edge_iterator_pair_t it = graph_ptr_->get_metadata().cell_edges<details::d2>(cell_id);
            for(; it.first != it.second; ++it.first)
            {
               if (!edge_predicate(it.first->second))
                  continue;
               result.push_back( (*it.first).second );
            } 
         }
      if (!result.empty())
         return true;
      for(int i = -d; i <= d; ++i)
         for(int j = -d; j <= d; ++j)
         {
            if (abs(i) != d && abs(j) != d)
               continue;
            details::cell_id_t cell_id = details::graph_metadata_t::get_cell_id<details::d4>(p);
            cell_id.first += i; cell_id.second += j;
            details::cell_edge_iterator_pair_t it = graph_ptr_->get_metadata().cell_edges<details::d4>(cell_id);
            for(; it.first != it.second; ++it.first)
            {
               if (!edge_predicate(it.first->second))
                  continue;
               result.push_back( (*it.first).second );
            } 
         }
      if (!result.empty())
         return true;
   }
   return false;
}
}
